/****************************************************************************
 * regfile.v
 ****************************************************************************/
// `include "defines.v"

/**
 * Module: regfile
 * 
 * 寄存器文件模块
 * 寄存器文件包含两个读端口，一个写端口；不含复位信号。
 * 寄存器文件由D触发器实现
 * 需要重点解释的是：寄存器ID的位宽为5，大于16个寄存器需要的位宽4，作用是寄存器可以按照16位
 *                存取，也可以按照32位存取，取决于idx的最高位是0(16位存取)还是1(32位存取)
 */

module regfile
#(
    parameter REG_NUM        = 16,
    parameter REG_ID_WIDTH   = 5,
    parameter REG_WIDTH      = 16
)
(
    input                        clk,

    // 寄存器文件读端口信号
    input  [REG_ID_WIDTH-1 : 0]  rd_src1_idx,
    input  [REG_ID_WIDTH-1 : 0]  rd_src2_idx,
    output [REG_WIDTH*2-1 : 0]   rd_src1_dat,
    output [REG_WIDTH*2-1 : 0]   rd_src2_dat,

    // 寄存器文件写端口信号
    input                        wr_dst_en,
    input  [REG_ID_WIDTH-1 : 0]  wr_dst_idx,
    input  [REG_WIDTH*2-1 : 0]   wr_dst_dat
);


    // 例化通用寄存器模块生成寄存器文件
    wire [REG_NUM-1:0]         reg_file_en;
    wire [REG_WIDTH-1 : 0]     reg_file_din  [REG_NUM-1 : 0];
    wire [REG_WIDTH-1 : 0]     reg_file_dout [REG_NUM-1 : 0];
    genvar i;
    generate
        for (i=0; i<REG_NUM; i=i+1)
        begin: regfile
            // 寄存器写使能，下面的逻辑可以化简，但从可读性上来说这么写会比较容易理解，可以依赖综合器去化简以保证代码可读性
            assign reg_file_en[i] = (((i == wr_dst_idx_rxl) | (i == wr_dst_idx_rxh)) & wr_dst_rx) 
                                  | ((i == wr_dst_idx_rhl) & wr_dst_rhl)
                                  & wr_dst_en;
            // 寄存器写入数据
            assign reg_file_din[i] = wr_dst_rhl & (i == wr_dst_idx_rhl) ? wr_dst_dat[REG_WIDTH-1 : 0] :
                                     wr_dst_rx  & (i == wr_dst_idx_rxl) ? wr_dst_dat[REG_WIDTH-1 : 0] :
                                     wr_dst_rx  & (i == wr_dst_idx_rxh) ? wr_dst_dat[REG_WIDTH*2-1 : REG_WIDTH] :
                                     {REG_WIDTH{1'b0}};
            // 寄存器实例化
            dffl #(REG_WIDTH) reg_file_dffl (clk, reg_file_en[i], reg_file_din[i], reg_file_dout[i]);
        end
    endgenerate


    // 寄存器文件读，两个读端口
    // 寄存器读idx的最高位为11则为RX，为10则为RN，为00 or 01则为RL或RH
    wire rd_src1_rx = rd_src1_idx[REG_ID_WIDTH-1 : REG_ID_WIDTH-2] == 2'b11;
    wire rd_src2_rx = rd_src2_idx[REG_ID_WIDTH-1 : REG_ID_WIDTH-2] == 2'b11;
    wire rd_src1_rn = rd_src1_idx[REG_ID_WIDTH-1 : REG_ID_WIDTH-2] == 2'b10;
    wire rd_src2_rn = rd_src2_idx[REG_ID_WIDTH-1 : REG_ID_WIDTH-2] == 2'b10;

    wire [2 : 0] rd_src1_idx_2_0 = rd_src1_idx[2 : 0];
    wire [2 : 0] rd_src2_idx_2_0 = rd_src2_idx[2 : 0];
    wire [3 : 0] rd_src1_idx_3_0 = rd_src1_idx[3 : 0];
    wire [3 : 0] rd_src2_idx_3_0 = rd_src2_idx[3 : 0];

    assign rd_src1_dat = rd_src1_rx ? {reg_file_dout[{1'b1, rd_src1_idx_2_0}], reg_file_dout[{1'b0, rd_src1_idx_2_0}]} : // RX
                         rd_src1_rn ? {(REG_WIDTH*2){1'b0}} :                                                            // RN
                                      {{REG_WIDTH{1'b0}} , reg_file_dout[rd_src1_idx_3_0]};                              // RL or RH
                                                                                                    
    assign rd_src2_dat = rd_src2_rx ? {reg_file_dout[{1'b1, rd_src2_idx_2_0}], reg_file_dout[{1'b0, rd_src2_idx_2_0}]} : 
                         rd_src2_rn ? {(REG_WIDTH*2){1'b0}} :
                                      {{REG_WIDTH{1'b0}} , reg_file_dout[rd_src2_idx_3_0]};
                                       

    // 寄存器文件写，只有一个写端口
    // 寄存器写idx的最高位为1则为32位，为0则为16位
    wire wr_dst_rx  = wr_dst_idx[REG_ID_WIDTH-1 : REG_ID_WIDTH-2] == 2'b11;
    wire wr_dst_rhl = ~wr_dst_idx[REG_ID_WIDTH-1];
    
    wire [REG_ID_WIDTH-2 : 0] wr_dst_idx_rxl = {1'b0, wr_dst_idx[2 : 0]}; // 32位数据写的低16位寄存器idx
    wire [REG_ID_WIDTH-2 : 0] wr_dst_idx_rxh = wr_dst_idx[3 : 0];         // 32位数据写的高16位寄存器idx
    wire [REG_ID_WIDTH-2 : 0] wr_dst_idx_rhl = wr_dst_idx_rxh;            // 16位数据写的寄存器idx，RL，RH

endmodule